#include <math.h>

RayTracing::RayTracing(Mundo *mundo,Camera *camera,int width,int height) {
	if (mundo) this->mundo = mundo;
	if (camera) this->camera = camera;
	this->width = width;
	this->height = height;
	this->imagem = new Image(width,height);
	
	if (this->camera && this->camera->towards && this->camera->up && (this->camera->distancia > 0) && this->camera->posicao) {
		this->tanTeta1 = (this->width/(2.0*this->camera->distancia));
		this->tanTeta2 = (this->height/(2.0*this->camera->distancia));
		
		Vector *tmp1,*tmp2,*tmp3,*tmp4;
		
		//distancia * vetorToward
		tmp1 = this->camera->towards->multiplicacao(this->camera->distancia);
		//distancia*tanTeta1*vetorRight
		tmp2 = this->camera->right->multiplicacao(this->camera->distancia*this->tanTeta1);
		
		//calculo de p1;
		tmp3 = this->camera->posicao->soma(tmp1);
		//Po + d*vetorTowards - d*tanTeta1*vetorRight
		this->p1 = tmp3->subtracao(tmp2);
		//Po + d*vetorTowards + d*tanTeta1*vetorRight
		this->p2 = tmp3->soma(tmp2);
		
		delete tmp2;//o valor de tmp2 deve ser recalculado para o angulo tanTeta2 e para o vetor up
		tmp2 = this->camera->up->multiplicacao(this->camera->distancia*this->tanTeta2);
		
		//Po + d*vetorTowards + d*tanTeta2*up
		this->p3 = tmp3->soma(tmp2);
		//Po + d*vetorTowards - d*tanTeta2*up
		this->p4 = tmp3->subtracao(tmp2);
		
		this->p2_p1 = this->p2->subtracao(p1);
		this->p3_p4 = this->p3->subtracao(p4);
		
		//delete tmp1; delete tmp2; delete tmp3; delete tmp4;
		
	}
}

RayTracing::~RayTracing() {
	if (mundo) delete mundo;
	if (camera) delete camera;
	if (imagem) delete imagem;
}

Ray* RayTracing::construirRaioPixel(int i,int j) {
	Vector *p,*tmp1,*tmp2,*tmp3,*tmp4;
	
	double ti,tj;
	ti = i;
	tj = j;
	
	tmp1 = this->p2_p1->multiplicacao((ti/this->width) + 0.5);
	tmp2 = this->p3_p4->multiplicacao((tj/this->height) + 0.5);
	
	
	tmp3 = this->p1->soma(tmp1);
	tmp4 = tmp3->soma(this->p4);
	Vector *posicao = tmp4->soma(tmp2);
	delete tmp1,tmp2,tmp3,tmp4;
	
	tmp1 = posicao->subtracao(this->camera->posicao);
	Vector *direcao = tmp1->vetorUnitario();
	delete tmp1;
	
	Ray *raio = new Ray(this->camera->posicao,direcao);
	return raio;
}

Ray* RayTracing::construirRaioPixel2(int i,int j) {
	
	i = i - width/2;
	j = j - height/2;

	Matrix3D* rotacaoX = Vector::rotacao( this->camera->up, atan(i / this->camera->distancia)); // matrix de rotacao em torno de up
	Matrix3D* rotacaoY = Vector::rotacao( this->camera->right, atan( j / this->camera->distancia )); // matrix de rotacao em torno de right
	
	Vector* direcao = this->camera->towards->multiplicacao(rotacaoY->multiplicacao(rotacaoX));
	Ray *raio = new Ray(this->camera->posicao,direcao->vetorUnitario());
	return raio;
}

IntersectObject* RayTracing::getIntersecsao(Ray *raio) {
	int i = 0;
	Object *obj;
	Intersect *inter,*melhor = NULL;
	Vector *vtmp;
	Object *melhorObj;

	for (i = 0;i < this->mundo->totalObjs();i++) {
		obj = this->mundo->getObjeto(i);
		
		if (obj) {
			inter = obj->intersecsao(raio);
			
			if (inter) {
				//avalia se a intersecsao é melhor do que a atual
				//cout << "dist = " << inter->distancia << "\n";
				if ((!melhor) || (inter->distancia < melhor->distancia)) {
					melhor = inter;
					melhorObj = obj;
				} else {
					delete inter;
					inter = NULL;
				}
			} else {
				
			}
		}
	}
	
	
	
	if (melhor) {
		IntersectObject *tmp = new IntersectObject(melhor->p,melhor->normal,melhorObj,melhor->distancia);
		delete melhor;
		return tmp;
	}
	else return NULL;
}

void RayTracing::render(int x,int y, int width, int height) {
	int larg = (x + width);
	int alt = (y + height);
	int i,j;
	Ray *raio;
	IntersectObject *inter;
	Object *obj;
	Color *cor;
	Vector *l,*r,*v;
	double c;
	double prod;
	for (i = x;i < larg;i++) {
		for (j = y;j < alt;j++) {
			//obtem o raio que passa atravez do pixel
			raio = construirRaioPixel2(i,j);
			//obtem a intersecsao mais proxima
			
			inter = getIntersecsao(raio);
			
			
			if (inter) {
				//cout << "intensidade = " << mundo->luz->intensidade << "\n";
				obj = inter->objeto;
				//vetor do ponto em direcao a luz
				l = mundo->luz->posicao->subtracao(inter->p)->vetorUnitario();
				//vetor r
				r = inter->normal->multiplicacao(inter->normal->produtoEscalar(l))->subtracao(l)->vetorUnitario();
				
				v = inter->p->subtracao(this->camera->posicao)->vetorUnitario();
				//realiza o cálculo de cor
				prod = pow(v->produtoEscalar(r),obj->nShing);
				c = 0;
				c = mundo->int_ambiente * obj->kAmbiente;
				
				c += mundo->luz->intensidade * (obj->kDifusa*(inter->normal->produtoEscalar(l)) + obj->kEspecular*prod);
				double tmp = (c/255.0);
				cor = new Color(tmp * obj->cor->r,tmp * obj->cor->g,tmp * obj->cor->b);
				
			} else {
				cor = mundo->fundo;
			}
			
			
			imagem->imageSetPixel(i,j,cor);
			//delete cor;
		}
	}
	imagem->imageWriteTGA("teste.tga");
}
